package com.dankegongyu.lib.common.rx;

import com.dankegongyu.lib.common.rx.model.ModelEvent;
import com.dankegongyu.lib.common.rx.presenter.PresenterEvent;
import com.trello.rxlifecycle3.LifecycleTransformer;
import com.trello.rxlifecycle3.OutsideLifecycleException;
import com.trello.rxlifecycle3.RxLifecycle;

import androidx.annotation.CheckResult;
import androidx.annotation.NonNull;
import io.reactivex.Observable;
import io.reactivex.functions.Function;

/**
 * @author wpq
 * @version 1.0
 */
public class RxLifecycleUtil {

    private RxLifecycleUtil() {
        throw new AssertionError("cannot be instantiated");
    }

    /**
     * Binds the given source to an Model lifecycle.
     *
     * @param lifecycle the lifecycle sequence of an Model
     * @return a reusable {@link LifecycleTransformer} that unsubscribes the source during the Model lifecycle
     */
    @NonNull
    @CheckResult
    public static <T> LifecycleTransformer<T> bindModel(@NonNull final Observable<ModelEvent> lifecycle) {
        return RxLifecycle.bind(lifecycle, MODEL_LIFECYCLE);
    }

    // Figures out which corresponding next lifecycle event in which to unsubscribe, for Models
    private static final Function<ModelEvent, ModelEvent> MODEL_LIFECYCLE = new Function<ModelEvent, ModelEvent>() {
        @Override
        public ModelEvent apply(ModelEvent lastEvent) throws Exception{
            switch (lastEvent) {
                case CREATE:
                    return ModelEvent.DESTROY;
                case DESTROY:
                    throw new OutsideLifecycleException("Cannot bind to Model lifecycle when outside of it.");
                default:
                    throw new UnsupportedOperationException("Binding to " + lastEvent + " not yet implemented");
            }
        }
    };

    /**
     * Binds the given source to an Presenter lifecycle.
     *
     * @param lifecycle the lifecycle sequence of an Presenter
     * @return a reusable {@link LifecycleTransformer} that unsubscribes the source during the Presenter lifecycle
     */
    @NonNull
    @CheckResult
    public static <T> LifecycleTransformer<T> bindPresenter(@NonNull final Observable<PresenterEvent> lifecycle) {
        return RxLifecycle.bind(lifecycle, PRESENTER_LIFECYCLE);
    }

    // Figures out which corresponding next lifecycle event in which to unsubscribe, for Presenters
    private static final Function<PresenterEvent, PresenterEvent> PRESENTER_LIFECYCLE = new Function<PresenterEvent, PresenterEvent>() {
        @Override
        public PresenterEvent apply(PresenterEvent lastEvent) throws Exception{
            switch (lastEvent) {
                case CREATE:
                    return PresenterEvent.DESTROY;
                case DESTROY:
                    throw new OutsideLifecycleException("Cannot bind to Presenter lifecycle when outside of it.");
                default:
                    throw new UnsupportedOperationException("Binding to " + lastEvent + " not yet implemented");
            }
        }
    };

}
